
let ( // ) = Filename.concat

let inst_dir = OKAHEP_config.install_dir

(* file where ocaml record is declared *)
let ta_decl = ref "Ttree_analog"
type opt = {
    id: string ref;
    mod_name: string ref;
    root_file: string ref;
    ttree_name: string ref;
    to_directory: string ref;
    lib: bool ref;
    only_lib: bool ref;
    only_texts: bool ref;
    debug: int ref;
}

let default_id = "ocaml_creation"
let opt = {
    id=ref default_id;
    mod_name = ref "";
    ttree_name=ref "";
    root_file=ref "";
    lib=ref false;
    only_lib=ref false;
    only_texts=ref false;
    to_directory = ref "";
    debug = ref 0;
}

type file = { name: string; contents: string }

let get_file ?(dir=Filename.current_dir_name) ?(process=fun s -> s) name =
    let full_name = (*inst_dir //*) dir // name in
    let handler = open_in full_name in
    let buffer = Buffer.create 1000 in
    Buffer.add_channel buffer handler (in_channel_length handler);
    let contents = process (Buffer.contents buffer) in
    {name=name; contents=contents}
;;

let copy ?dir ?process ~dest ?(to_file=None) name =
    if not (Sys.file_exists dest) then (
        print_string ("creating directory '"^dest^"'... ");
        (try
                Unix.mkdir dest 0o744;
                print_endline "OK"
        with e -> (
            print_endline ("Received exception"^
            "in directory '"^(Sys.getcwd ())^" while copying file "^name);
            raise e
        )
        )
    );
    let file = get_file ?dir ?process name in
    let out = open_out (dest // 
        (match to_file with None -> file.name | Some a -> a)) in
    if !(opt.debug) >= 1 then Printf.printf "copy %s to %s\n" name dest;
    output_string out file.contents;
    close_out out
;;

(*let okahep_lib = ["OKAHEP.cmx"; "OKAHEP.cmi" ]*)
(*let okahep_lib = [
    "Glob.cmx"; "Glob.cmi"; "Glob.o";
    "Ocaml_dbm.cmx"; "Ocaml_dbm.cmi"; "Ocaml_dbm.o";
    "Db_dir.cmx"; "Db_dir.cmi"; "Db_dir.o";
]*)
let ottree_gen_files = [
    "c_fill_record.cpp"; "Makefile"; "ttree_creation.cpp"; 
    "ttree_declLinkDef.h"; "TTreePlayer_new.cxx"; 
   (* "ocaml_creation.ml";*) "ttree_decl_stubs.cpp"; "fill.ml";
   "gen_class.sh";
]

let make_replacements lst s =
    let result = ref s in
    List.iter (fun (reg_str,repl) ->
        result := Str.global_replace (Str.regexp reg_str) repl !result
    ) lst;
    !result
;;


let copy_all () =
    Random.self_init();
    let tmp_dir = Filename.temp_dir_name //
   (* max_int leads to error on 64-bit 
        ("ottree_gen_" ^ (string_of_int (Random.int max_int))) in *)
        ("ottree_gen_" ^ (string_of_int (Random.int 1_000_000_000))) in
(*    let cp = copy ~dir:(inst_dir // "common") ~dest:tmp_dir in*)
    let cp' = copy ~dir:OKAHEP_config.ottree_gen_dir ~dest:tmp_dir in
(*    List.iter cp okahep_lib; *)
(*    let print_makefile s = Str.global_replace 
      (Str.regexp "\$(INST_DIR_COMMON)") (inst_dir // "common") s in
    cp' ~process:print_makefile "Makefile";*)
    let replace = make_replacements [
        "\$(INST_DIR_COMMON)", inst_dir // "common";
        "\$(MOD_ID)", !(opt.id);
        "\$(MOD_NAME)", !(opt.mod_name);
        "\$(TA_DECL)", !ta_decl;
    ] in
    cp' ~to_file:(Some (!(opt.id) ^ "_inc.ml")) ~process:replace "ocaml_creation.ml";
    List.iter (fun name -> cp' ~process:replace name) ottree_gen_files;
    copy ~dir:OKAHEP_config.install_dir ~dest:tmp_dir "myocamlbuild.ml";
(*    cp' ~process:replace_makefile "Makefile";*)
    tmp_dir
;;

let set r v () = r := v
let options = [
    "-id", Arg.String ((:=)opt.id), "str it's ocaml module id string";
    "-ttree", Arg.String ((:=)opt.ttree_name), "TNAME ttree name";
    "-lib", Arg.Unit (set opt.lib true), " copy ocaml library";
    "-only-lib", Arg.Unit (set opt.only_lib true), 
        " create and copy ocaml library (without writing converted data)";
    "-only-texts", Arg.Unit (set opt.only_texts true), 
        " copy ocaml library (without compiling library or 
        writing converted data)";
    "-debug", Arg.Int ((:=) opt.debug), 
        ("n \t n=1 print messages \n\t"^
        "n>=2 do not delete temporary directory (/tmp/ottree_gen_****)");
    "-to", Arg.String ((:=) opt.to_directory), 
        ("Dir Dir is directory where all files will be copied to. \n"^
        "\tBy default <file.root>_<TNAME> will be used");
]
let test_id () =
    if ( !(opt.lib) || !(opt.only_lib)) && !(opt.id) = default_id then (
        Printf.printf ("\n\nWARNING: option '-id name' is missed.\n"^^ 
          "Name '%s' will be used by default\n\n") default_id
    )

let parse_arg () =
    let usage = Printf.sprintf "Usage: %s [options] <file.root>"
       Sys.argv.(0) in
    Arg.parse (Arg.align options) ((:=) opt.root_file) usage;
    if Filename.is_relative !(opt.root_file) 
    (* if root_file is starting from protocol name, e.g. "rfio://", then
       is_relative will return true, so we need check existence of the file *)
      && Sys.file_exists !(opt.root_file) then (
        opt.root_file := (Sys.getcwd()) // !(opt.root_file)
    ) else ();
    if !(opt.ttree_name) = "" then (
        Arg.usage options usage;
        failwith "flag -ttree is obligatory!"
    );
    test_id();
    opt.mod_name := String.capitalize !(opt.id);
    if !(opt.id) <> default_id then 
        ta_decl := Printf.sprintf "Ttree_%s" !(opt.mod_name);
    if !(opt.to_directory) = "" then (
        opt.to_directory := !(opt.root_file) ^ "_" ^ !(opt.ttree_name)
    ) else if Filename.is_relative !(opt.to_directory) then (
            opt.to_directory := Sys.getcwd () // !(opt.to_directory)
    )
;;

let () =
    parse_arg();
    Printf.printf "OKAHEP lib is installed in %s\n" inst_dir; flush stdout;
    let tmp_dir = copy_all () in
    Sys.chdir tmp_dir;
    let cmd_prepare = Printf.sprintf "./TTreePlayer_new %s %s -idl %s.idl" 
        !(opt.root_file) !(opt.ttree_name) !ta_decl in
    let cmd_convert = Printf.sprintf "%s %s %s %s" (tmp_dir // "convert")
        !(opt.root_file) !(opt.ttree_name) !(opt.to_directory) in
    let id = !(opt.id) in
    let copy_lib () = List.iter (fun file_name ->
        copy ~dir:tmp_dir ~dest:!(opt.to_directory) file_name
    ) [ (!ta_decl ^ ".cmi"); (!ta_decl ^ "_obj.cmi"); id^".cmi"; 
        "dll"^id^".so"; "lib"^id^".a"; id^".a"; id^".cmxa";  id^".cma"; ] in
    let root_link = "ttree_decl_" ^ !ta_decl ^ "_root_link" in
    let code_files = (!(opt.id) ^ ".ml") :: "myocamlbuild.ml" :: 
        (!ta_decl ^".ml") :: (!ta_decl ^ ".mli") :: "ttree_declaration.h" ::
            (!ta_decl ^ "_obj.ml") :: (!ta_decl ^ "_stubs.cpp") ::
                ("lib"^ !ta_decl ^".clib") :: 
                    (root_link^".cpp") :: (root_link^".h") ::
                    ottree_gen_files in
    let copy_code () = List.iter (fun file_name ->
        copy ~dir:tmp_dir ~dest:!(opt.to_directory) file_name 
    ) code_files in
    let clean_up () = Array.iter 
        (fun file_name -> Sys.remove (tmp_dir // file_name))
        (Sys.readdir tmp_dir);
        Unix.rmdir tmp_dir
    in
    let last_command = ref "" in
    let command cmd = (
        last_command := cmd;
        if !(opt.debug) > 0 then 
            Printf.printf "execute command '%s'\n" cmd; flush stdout;
        Sys.command cmd = 0
    ) in
    if 
        command "make TTreePlayer_new" &&
        command cmd_prepare &&
        command "make clib" &&
        command ("make " ^ root_link ^ ".cpp") &&
        command ("make " ^ !ta_decl ^ "_stubs.cpp") &&
        command ("make " ^ !ta_decl ^ "_obj.ml") &&
        (if not !(opt.only_texts) 
        then (
            command "make" &&
            command "make lib" &&
            command "make o" &&
            (if !(opt.only_lib) || !(opt.only_texts) 
            then true else command cmd_convert) &&
            (if !(opt.lib) || !(opt.only_lib) then copy_lib (); true))
        else true) &&
        (if !(opt.only_texts) then copy_code (); true) &&
        (if !(opt.debug) < 2 then clean_up (); true) 
    then ()
    else (failwith ("command'"^ !last_command ^ 
    "' exited with non-zero exit code"));
    test_id()
;;

